/**
 * 发布/订阅模式，基于Qt信号槽实现
 *
 * QML使用方法：
 *  先向QML注册PSAdapter类和PubSub类，再向QML engine添加PubSub实例pubsub
 *  然后使用Connections，类似如下：
    import PubSub 1.0
    Connections {
        target: pubsub.adapter(PubSub.Topic_XXX)
        onNotify: (_, value) => {
            // 代码块
        }
    }
 */

#ifndef PUBSUB_H
#define PUBSUB_H

#include <QDebug>
#include <QObject>
#include <QList>
#include <QVariant>
#include <QMutex>
#include <QMetaType>
#include <QMap>
#include <QMultiMap>

class PSAdapter : public QObject
{
    Q_OBJECT
public:
    explicit PSAdapter(QObject *parent = nullptr): QObject{parent} {}

signals:
    void notify(int topic, QVariant arg);
};

class PubSub : public QObject
{
    Q_OBJECT
public:
    enum Topic {
        Topic_Test,
        Topic_EnterMainWin,
        Topic_UserActive,
        Topic_StandbyEnabled,
        Topic_StandbyTime,
        Topic_Standby,
        Topic_FlowId,
        Topic_CleanCardState,

        Topic_Max
    };
    Q_ENUM(Topic)

    explicit PubSub(QObject *parent = nullptr);
    static PubSub &getInstance();

    Q_INVOKABLE PSAdapter *adapter(int topic) const;
    void subscribe(int topic, const QObject *subscriber, const char *slotName, Qt::ConnectionType connectionType = Qt::AutoConnection);
    void unsubscribe(int topic, const QObject *subscriber);
    void unsubscribe(const QObject *subscriber);
    Q_INVOKABLE void publish(int topic, QVariant arg = QVariant());

private:
    struct SubscriberInfo {
        int topic;
        const QObject *subscriber;
        QByteArray slot;

        SubscriberInfo(int topic, const QObject *subscriber, const QByteArray &slot) {
            this->topic = topic;
            this->subscriber = subscriber;
            this->slot = slot;
        }

        bool operator==(const SubscriberInfo &other) {
            return this->topic == other.topic &&
                   this->subscriber == other.subscriber &&
                   this->slot == other.slot;
        }
    };

    QMutex mutex;
    PSAdapter *defaultAdapter;
    QMap<int, PSAdapter *> adapters;
    QList<SubscriberInfo> subscribers;

private slots:
    void onTest(int topic, QVariant arg);
};

#endif // PUBSUB_H
